#pragma once 

#include "..\CoreClasses\GeometricObjects\GeometricObjects.h"
#include "..\Scene\SceneFactory.h"
#include "..\Tracer\DepthTracer.h"
#include "..\Light\Light.h"
#include "..\CoreClasses\UtilClasses\Common.h"
#include "..\Material\Reflective.h"
#include "..\Integrators\IrradianceCache.h"
#include "..\GuiHelper\gw.h"
#include "..\CoreClasses\Logger\Logger.h"
#include "..\CoreClasses\Camera\Camera.h"
#include "..\CoreClasses\GeometricObjects\Objects.h"
#include "..\CoreClasses\RGBObject\RGBColor.h"
#include "..\Tracer\Tracer.h"
#include "..\World\View Plane Details\ViewPlane.h"

#include <Peer/Peer2Peer-Peer/P2pPeer.h>

#define GLOBAL_ILLUMINATION

using namespace Engine::Common;

namespace Engine
{
	class AbstractWorld
	{
	public:
		virtual bool RenderScene(void) = 0;
		virtual RGBColor GetShade(ShadeRec& sr, int depth) = 0;
	
		//----------------------------------------------------------------------------------------------------------------
		//		 Method Name: CONSTRUCTOR
		//		Return Value: VOID
		//		  Parameters: VOID
		//			  Status: READY
		//----------------------------------------------------------------------------------------------------------------
		AbstractWorld(void) : tracer_ptr(NULL)
		{
			backgroundColor = RGBColor(0, 0, 0);
		}
		//----------------------------------------------------------------------------------------------------------------
		//		 Method Name: CONSTRUCTOR
		//		Return Value: VOID
		//		  Parameters: int Scene Type
		//			  Status: DEPECRATED
		//----------------------------------------------------------------------------------------------------------------
		AbstractWorld(int m_sceneType) : sceneType(m_sceneType)
		{
		}
		//----------------------------------------------------------------------------------------------------------------
		//		 Method Name: CONSTRUCTOR
		//		Return Value: VOID
		//		  Parameters: int Scene Type, int Width, int Height
		//			  Status: READY
		//----------------------------------------------------------------------------------------------------------------
		AbstractWorld(int m_sceneType, int m_width, int m_height) : sceneType(m_sceneType)
		{
			vp.hres = m_width;
			vp.vres = m_height;
			vp.s = 1;
		}
		//----------------------------------------------------------------------------------------------------------------
		//		 Method Name: CONSTRUCTOR
		//		Return Value: VOID
		//		  Parameters: int Scene Type, int width, int height, string Image path
		//			  Status: READY
		//----------------------------------------------------------------------------------------------------------------
		AbstractWorld(int m_sceneType, int m_width, int m_height, string p_ImagePath) : sceneType(m_sceneType)
		{
			vp.hres = m_width;
			vp.vres = m_height;
			vp.s = 1;
		}
		//----------------------------------------------------------------------------------------------------------------
		//		 Method Name: ClampToColor
		//		Return Value: RGBColor
		//		  Parameters: RGBColor
		//			  Status: READY
		//----------------------------------------------------------------------------------------------------------------
		RGBColor ClampToColor(RGBColor rawColor) const
		{
			RGBColor c(rawColor);

			if (rawColor.r > 1.0 || rawColor.g > 1.0 || rawColor.b > 1.0) {
				c.r = 1.0; c.g = 0.0; c.b = 0.0;
			}

			return (c);
		}
		//----------------------------------------------------------------------------------------------------------------
		//		 Method Name: CONSTRUCTOR
		//		Return Value: RGBColor
		//		  Parameters: RGBColor
		//			  Status: READY
		//----------------------------------------------------------------------------------------------------------------
		RGBColor MaxToOne(RGBColor c) const
		{
			float max_value = max(c.r, max(c.g, c.b));

			if (max_value > 1.0)
				return (c / max_value);
			else
				return (c);
		}
		//----------------------------------------------------------------------------------------------------------------
		//		 Method Name: GetBuffer
		//		Return Value: Pixel*
		//		  Parameters: VOID
		//			  Status: DEPCRATED
		//----------------------------------------------------------------------------------------------------------------
		Pixel* GetBuffer()
		{
			return buffer;
		}

		RGBColor* GetRgbBuffer()
		{
			return rgbBuffer;
		}

		Scene* GetScene()
		{
			return currentScene;
		}

		void SetCurrentY(int p_currentY)
		{
			m_currentY = p_currentY;
		}

		int GetCurrentY()
		{
			return m_currentY;
		}

		Camera* GetCamera()
		{
			return m_camera;
		}

		void SetCamera(Camera* p_camera)
		{
			m_camera = p_camera;
		}

		//----------------------------------------------------------------------------------------------------------------
		//		 Method Name: build
		//		Return Value: VOID
		//		  Parameters: VOID
		//			  Status: READY
		//----------------------------------------------------------------------------------------------------------------
		//virtual void Build(void)
		//{
		//	int numSamples = 25;
		//	vp.gamma = 1.0;
		//	vp.s = 1.0;
		//	//vp.SetSampler(new Jittered(numSamples));
		//	eye = 500;
		//	buffer = new Pixel[vp.hres * vp.vres];
		//	rgbBuffer = new RGBColor[vp.hres * vp.hres];

		//	int size = vp.hres * vp.vres;
		//	backgroundColor = RGBColor(255, 0, 0);
		//	for (int index = 0; index < size; index++)
		//	{
		//		buffer[index] = 0;
		//	}

		//	currentScene = SceneFactory::GetScene(sceneType);
		//	tracer_ptr = new DepthTracer(currentScene, true);
		//	tracer_ptr->UseAccelerationStructure();
		//	integrator = new IrradianceCache(currentScene, tracer_ptr->GetAccelerationStructure(), tracer_ptr);
		//	//focusing on the yellow ball at the moment
		//	m_camera = currentScene->GetCamera();
		//	Logger::LogTrace(m_camera->GetLookAt().toString());
		//	//progressive rendering
		//	m_currentPixel = buffer;
		//	m_currentY = 0;
		//}

		virtual void Build(int p_scenePath)
		{
			int numSamples = 25;
			vp.gamma = 1.0;
			vp.s = 1.0;
			//vp.SetSampler(new Jittered(numSamples));
			eye = 500;
			buffer = new Pixel[vp.hres * vp.vres];
			rgbBuffer = new RGBColor[vp.hres * vp.hres];

			int size = vp.hres * vp.vres;
			backgroundColor = RGBColor(255, 0, 0);
			for (int index = 0; index < size; index++)
			{
				buffer[index] = 0;
			}

			GetScene(p_scenePath);

			//focusing on the yellow ball at the moment
			m_camera = currentScene->GetCamera();
			Logger::LogTrace(m_camera->GetLookAt().toString());
			//progressive rendering
			m_currentPixel = buffer;
			m_currentY = 0;
		}
		virtual void Build(string p_randomKey, string p_configPath)
		{
			int numSamples = 25;
			vp.gamma = 1.0;
			vp.s = 1.0;
			//vp.SetSampler(new Jittered(numSamples));
			eye = 500;
			buffer = new Pixel[vp.hres * vp.vres];
			rgbBuffer = new RGBColor[vp.hres * vp.hres];

			int size = vp.hres * vp.vres;
			backgroundColor = RGBColor(255, 0, 0);
			for (int index = 0; index < size; index++)
			{
				buffer[index] = 0;
			}

			GetScene(sceneType, p_randomKey, p_configPath);

			//focusing on the yellow ball at the moment
			m_camera = currentScene->GetCamera();
			Logger::LogTrace(m_camera->GetLookAt().toString());
			//progressive rendering
			m_currentPixel = buffer;
			m_currentY = 0;
		}



		virtual void Build()
		{
			int numSamples = 25;
			vp.gamma = 1.0;
			vp.s = 1.0;
			//vp.SetSampler(new Jittered(numSamples));
			eye = 500;
			buffer = new Pixel[vp.hres * vp.vres];
			rgbBuffer = new RGBColor[vp.hres * vp.hres];

			int size = vp.hres * vp.vres;
			backgroundColor = RGBColor(255, 0, 0);
			for (int index = 0; index < size; index++)
			{
				buffer[index] = 0;
			}

			//focusing on the yellow ball at the moment
			m_camera = currentScene->GetCamera();
			Logger::LogTrace(m_camera->GetLookAt().toString());
			//progressive rendering
			m_currentPixel = buffer;
			m_currentY = 0;
		}

		void GetScene(int p_pathNumber)
		{
			currentScene = SceneFactory::GetScene(sceneType, p_pathNumber);
			tracer_ptr = new DepthTracer(currentScene, true);
			tracer_ptr->UseAccelerationStructure();
			integrator = new IrradianceCache(currentScene, tracer_ptr->GetAccelerationStructure(), tracer_ptr);
		}

		void GetScene(int p_pathNumber, string p_randomStringSeed, string p_pathConfigFile)
		{
			currentScene = SceneFactory::GetScene(sceneType, p_randomStringSeed, p_pathConfigFile);
			tracer_ptr = new DepthTracer(currentScene, true);
			tracer_ptr->UseAccelerationStructure();
			integrator = new IrradianceCache(currentScene, tracer_ptr->GetAccelerationStructure(), tracer_ptr);
		}

		///-------------------------------------------------------------------------------------------------
		/// <summary>	Builds this object. </summary>
		///
		/// <remarks>	Adrian, 21/09/2015. </remarks>
		///-------------------------------------------------------------------------------------------------

		virtual void Build(P2pPeer<RadiancePoint, IrradianceCache>* p_peer)
		{
			int numSamples = 25;
			vp.gamma = 1.0;
			vp.s = 1.0;
			//vp.SetSampler(new Jittered(numSamples));
			eye = 500;
			buffer = new Pixel[vp.hres * vp.vres];
			rgbBuffer = new RGBColor[vp.hres * vp.hres];

			int size = vp.hres * vp.vres;
			backgroundColor = RGBColor(255, 0, 0);
			for (int index = 0; index < size; index++)
			{
				buffer[index] = 0;
			}


			currentScene = SceneFactory::GetScene(sceneType);
			tracer_ptr = new DepthTracer(currentScene, true);
			tracer_ptr->UseAccelerationStructure();
			integrator = new IrradianceCache(currentScene, tracer_ptr, p_peer);
			//focusing on the yellow ball at the moment
			m_camera = currentScene->GetCamera();
			Logger::LogTrace(m_camera->GetLookAt().toString());
			//progressive rendering
			m_currentPixel = buffer;
			m_currentY = 0;
		}

		///-------------------------------------------------------------------------------------------------
		/// <summary>	Builds the given p camera. </summary>
		///
		/// <remarks>	Adrian, 21/09/2015. </remarks>
		///
		/// <param name="p_camera">	[in,out] If non-null, the camera. </param>
		///-------------------------------------------------------------------------------------------------

		virtual void Build(Camera* p_camera)
		{
			int numSamples = 25;
			vp.gamma = 1.0;
			vp.s = 1.0;
			//vp.SetSampler(new Jittered(numSamples));
			eye = 500;
			buffer = new Pixel[vp.hres * vp.vres];
			rgbBuffer = new RGBColor[vp.hres * vp.hres];
			int size = vp.hres * vp.vres;

			for (int index = 0; index < size; index++)
			{
				buffer[index] = 0;
			}
			backgroundColor = RGBColor(0, 0, 0);
			currentScene = SceneFactory::GetScene(sceneType);
			tracer_ptr = new DepthTracer(currentScene, true);
			integrator = new IrradianceCache(currentScene, tracer_ptr);
			tracer_ptr->UseAccelerationStructure();
			m_camera = p_camera;
			m_currentPixel = buffer;
			m_currentY = 0;
		}

		///-------------------------------------------------------------------------------------------------
		/// <summary>	Builds the given p camera. </summary>
		///
		/// <remarks>	Adrian, 21/09/2015. </remarks>
		///
		/// <param name="p_camera">	[in,out] If non-null, the camera. </param>
		///-------------------------------------------------------------------------------------------------

		virtual void Build(Camera* p_camera, P2pPeer<RadiancePoint, IrradianceCache>* p_peer)
		{
			int numSamples = 25;
			vp.gamma = 1.0;
			vp.s = 1.0;
			//vp.SetSampler(new Jittered(numSamples));
			eye = 500;
			buffer = new Pixel[vp.hres * vp.vres];
			rgbBuffer = new RGBColor[vp.hres * vp.hres];
			int size = vp.hres * vp.vres;

			for (int index = 0; index < size; index++)
			{
				buffer[index] = 0;
			}
			backgroundColor = RGBColor(0, 0, 0);
			currentScene = SceneFactory::GetScene(sceneType);
			tracer_ptr = new DepthTracer(currentScene, true);
			integrator = new IrradianceCache(currentScene, tracer_ptr, p_peer);
			tracer_ptr->UseAccelerationStructure();
			m_camera = p_camera;
			m_currentPixel = buffer;
			m_currentY = 0;
		}
		///-------------------------------------------------------------------------------------------------
		/// <summary>	Move's camera. </summary>
		///
		/// <remarks>	Adrian, 28/08/2015. </remarks>
		///-------------------------------------------------------------------------------------------------
		void MoveCamera()
		{
			m_camera->MoveCamera();
		}

		bool CheckIfMoreFrames()
		{
			return m_camera->m_path->CheckMorePoints();
		}

		IrradianceCache* GetIrradainceCache()
		{
			return integrator;
		}

	protected:
		ViewPlane vp;
		RGBColor backgroundColor;
		Tracer* tracer_ptr;
		Pixel *buffer;
		Scene *currentScene;
		int sceneType;
		int eye;
		IrradianceCache* integrator;
		Camera* m_camera;
		int d;
		int path;

		int m_currentY;
		Pixel* m_currentPixel;
		RGBColor *rgbBuffer;
		Logger m_logFile;
		ShadeRec m_currentShadrec;

	private:
	};
}